#include<iostream>
#include<sstream>
#include<fstream>
#include<cstdlib>
using namespace std;
const char * rs_mask;
bool * snpmask;
const int PLINK_ID1 = 108;
const int PLINK_ID2 = 27;
const int PLINK_SNP_MAJOR=1;
const int PLINK_PERSON_MAJOR=0;
const int PLINK_WT_HOMO=0;
const int PLINK_MUT_HOMO=3;
const int PLINK_HET=2;
const int PLINK_MISSING=1;
const char GARY_MISSING=0;
const char GARY_WT_HOMO=1;
const char GARY_HET=2;
const char GARY_MUT_HOMO=3;

int totalrows;
int totalcols;
bool snpmajor;

void plink2gary(){
  //ifstream ifs("genotypes.bed",ios::in|ios::binary);
  //ofstream ofs("genotypes.new",ios::out|ios::binary);
  //if (!ifs.is_open() && !ofs.is_open()){
  //  cerr<<"plink.txt not found or plink.bed could not open. exiting\n";
  //  exit(1);
  // }
  bool remainder = (totalcols % 4!=0)?true:false;
  int veclen = totalcols/4+remainder;
  char vector_read[veclen];
  int masks[4] = {3,12,48,192};
  char ** outmatrix;
  for (int row=0;row<totalrows;++row){
    cin.read(vector_read,veclen);
    if (!snpmajor ||  snpmask[row]){
      int maskindex = 0;
      //int byteindex = 0;
      int col = 0;
      for (int byteindex =0;byteindex<veclen;++byteindex){
        for(int pair=0;pair<4;++pair) {
          int val = masks[pair] & vector_read[byteindex];
          for(int shift=0;shift<pair;++shift) {
            val = val>>2;
            //cerr<<"shifting\n";
          }
          //cerr<<"row "<<row<<" snp "<<snp<<"\t"<<(int)vector_read[byteindex]<<endl;
          //cerr<<"row "<<row<<" snp "<<snp<<"\t"<<val<<endl;
          char garygeno;
          switch(val){
            case PLINK_MUT_HOMO:
              garygeno = GARY_MUT_HOMO;
              break;
            case PLINK_HET:
              garygeno = GARY_HET;
              break;
            case PLINK_WT_HOMO:
              garygeno = GARY_WT_HOMO;
              break;
            case PLINK_MISSING:
              garygeno = GARY_MISSING;
              break;
          }
          if (col<totalcols && (snpmajor || snpmask[col])) cout<<(int)garygeno;
          ++col;
        }
      }
      cout<<endl;
      cerr<<"Row: "<<row<<" completed.\n";
    }else{
      cerr<<"Row: "<<row<<" skipped.\n";
    }
  }
  //ofs.close();
  //cin.close();
}


void testing2(){
  char testread[4];
  ifstream ifs("genotypes.imputed",ios::in|ios::binary);
  ifs.seekg(4,ios::cur);
  //ifs.read(testread,4);
  //for(int i=0;i<3;++i){
  //  cerr<<i<<" "<<testread[i]<<endl;
  //}
  ifs.seekg(4,ios::cur);
  ifs.read(testread,4);
  for(int i=0;i<3;++i){
    cerr<<i<<" "<<testread[i]<<endl;
  }
  ifs.seekg(4,ios::cur);
  ifs.seekg(4,ios::cur);
  ifs.seekg(4,ios::cur);
  if (!ifs.eof()) cerr<<"eof\n";
  ifs.close();
}

void testing(){
  char testread1[3];
  char testread2[4];
  ifstream ifs("genotypes.bed",ios::in|ios::binary);
  ifs.read(testread1,3);
  ifs.read(testread2,4);
  ifs.close();
  int masks[4] = {3,12,48,192};
  for(int i=0;i<4;++i){
    //if (i<3) cerr<<"PLINK ID: "<<(int)testread[i]<<endl;
    for(int j=0;j<4;++j){
      int val = masks[j] & testread2[i];
      for(int k=0;k<j;++k) val = val>>2;
      cerr<<i<<"\t"<<val<<endl;
    }
  }
  ofstream ofs("test.bin",ios::out|ios::binary);
  char  testchar[3] ;
  testchar[0]=1;
  testchar[1]=2;
  testchar[2]=3;
  ofs.write(testchar,3);
  testchar[0]=11;
  testchar[1]=21;
  testchar[2]=31;
  ofs.write(testchar,3);
  ofs.close();
}

int main(int argc,char * argv[]){
  bool usemask = argc>=4;
  if (argc<3){
    cerr<<"Arguments (optional: rslist_maskfile): \n";
    cerr<<"<persons> <snps>\n";
    //cerr<<"For individual major: <rows> <snps> <rslist_maskfile>\n";
    //cerr<<"For snp major: <snps> <rows> <rslist_maskfile>\n";
    exit(1);
  }
  // read and ignore header
  char vector_read[3];
  cin.read(vector_read,3);
  snpmajor = (bool)vector_read[2];
  cerr<<"SNP_MAJOR_MODE:\t"<<snpmajor<<endl;
  //cerr<<"debug\t"<<(int)vector_read[3]<<endl;
  //exit(0);
  snpmask = NULL;
  if (snpmajor){
    totalrows = atoi(argv[2]);
    snpmask = new bool[totalrows];
    for(uint i=0;i<totalrows;++i) snpmask[i] = true;
    totalcols = atoi(argv[1]);
    if (usemask) {
      rs_mask = argv[3];
      ifstream ifs(rs_mask);
      if (!ifs.is_open()){
        cerr<<"Sorry, cannot open "<<rs_mask<<endl;
        exit(1);
      }
      string line;
      int i=0;
      while(getline(ifs,line)){
        istringstream iss(line);
        iss>>snpmask[i++];
      }
    }
  }else{
    totalrows = atoi(argv[1]);
    totalcols = atoi(argv[2]);
  }
  plink2gary();
  if (snpmask!=NULL) delete[]snpmask;
}
